#include <math.h>
#include "Vector3.h"
#include "../Coretypes.h"

#pragma once
namespace CoreMath
{
	struct Vector4
	{
		real i, j, k, v;

		/* Constructors. */
		Vector4() {}

		Vector4(real i, real j, real k, real v)
		{
			this->i = i;
			this->j = j;
			this->k = k;
			this->v = v;
		}

		Vector4(const Vector4& toCopy)
		{
			this->i = toCopy.i;
			this->j = toCopy.j;
			this->k = toCopy.k;
			this->v = toCopy.v;
		}

		Vector4(const Vector3& fromVector3)
		{
			this->i = fromVector3.i;
			this->j = fromVector3.j;
			this->k = fromVector3.k;
		}

		/* Operators. */
		inline Vector4 operator=(const Vector4& toAssign)
		{
			this->i = toAssign.i;
			this->j = toAssign.j;
			this->k = toAssign.k;
			this->v = toAssign.v;
			return *this;
		}

		inline bool operator==(const Vector4& toCompare) const
		{
			return (this->i == toCompare.i) && (this->j == toCompare.j) && (this->k == toCompare.k) && (this->v == toCompare.v);
		}

		inline bool operator!=(const Vector4& toCompare) const
		{
			return !(this == &toCompare);
		}

		inline Vector4 operator*(const real multiplier) const
		{
			return Vector4(this->i*multiplier, this->j * multiplier, this->k * multiplier, this->v * multiplier);
		}

		inline Vector4 operator+(const Vector4& toAdd) const
		{
			return Vector4(this->i + toAdd.i, this->j + toAdd.j, this->k + toAdd.k, this->k + toAdd.k);
		}

		inline Vector4 operator-(const Vector4& toSubtract) const
		{
			return Vector4(this->i - toSubtract.i, this->j - toSubtract.j, this->k - toSubtract.k, this->v - toSubtract.v);
		}

		inline Vector4 operator-() const
		{
			return *this * -1;
		}

		/* Common vector operations. */
		inline real Magnitude(void) const
		{
			return sqrt(this->SqrMagnitude());
		}

		inline real SqrMagnitude(void) const
		{
			return (this->i * this->i) + (this->j * this->j) + (this->k * this->k) + (this->v * this->v);
		}

		inline void Normalize(void)
		{
			real vectMagnitude = this->Magnitude();

			if (!vectMagnitude)
				return;

			this->i /= vectMagnitude;
			this->j /= vectMagnitude;
			this->k /= vectMagnitude;
			this->v /= vectMagnitude;
		}

		inline static Vector4 Normalize(const Vector4& toNormalize)
		{
			real vectMagnitude = toNormalize.Magnitude();
			return vectMagnitude != 0 ? Vector4(toNormalize.i / vectMagnitude, toNormalize.j / vectMagnitude, toNormalize.k / vectMagnitude, toNormalize.v / vectMagnitude) : toNormalize;
		}

		inline static real DotProduct(const Vector4& v1, const Vector4& v2)
		{
			real DotProduct = 0;
			DotProduct += (v1.i * v2.i);
			DotProduct += (v1.j * v2.j);
			DotProduct += (v1.k * v2.k);
			DotProduct += (v1.v * v2.v);

			return DotProduct;
		}

		inline real AngleBetween(Vector4 v1, Vector4 v2)
		{
			v1.Normalize();
			v2.Normalize();
			real product = DotProduct(v1, v2);
			return acos(product);
		}
	};
}
